/*
Copyright (c) 2010 k.schulz, cmueller

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

The Software shall be used for Good, not Evil.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package de.altitudecustomcommands;


import de.altitude.cimuelle.Config;
import de.altitude.cimuelle.utils.ServerOperations;
import org.apache.log4j.Logger;

import java.io.*;
import java.util.LinkedList;
import java.util.List;


/**
 * This class provides methods for working with the players data, which are
 * playing on your server.
 *
 * @author k.schulz
 * @author c.mueller
 */
public class playerHandler implements Serializable {

    private List<Player> playerList = new LinkedList<Player>();

    private static final long serialVersionUID = 1000000002L;
    private static final Logger logger = Logger.getLogger("playerHandler");

    /**
     * Construct a new playerHandler.
     */

    public playerHandler() {
        this.playerList = new LinkedList<Player>();
        //loadPersistentList();
        //savePersistantListHandler();

    }

    private void savePersistantListHandler() {
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                logger.info("Shutting down, persisting playerList");
                try {
                    FileOutputStream fos;
                    fos = new FileOutputStream(Config.getInstance().getConfig("playerListFileName"));
                    ObjectOutputStream out = new ObjectOutputStream(fos);
                    out.writeObject(playerList);
                    logger.info("Object Persisted");
                    out.close();
                } catch (FileNotFoundException e) {
                    // expected
                    logger.info("No File specified. 'To save current players use 'playerListFileName' in property file to insert a filename.");
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        });
    }

    private void loadPersistentList() {
        try {
            FileInputStream fis;
            try {
                fis = new FileInputStream(Config.getInstance().getConfig("playerListFileName"));
                ObjectInputStream in = new ObjectInputStream(fis);
                //noinspection unchecked
                this.playerList = (LinkedList<Player>) in.readObject();
                in.close();
                logger.info("Object loaded from Filesystem.(" + playerList.size() + " entrys)");
                if (Config.getInstance().isDebugVerbose()) {
                    for (Player pl : playerList) {
                        logger.debug(pl.toString());
                    }
                }
            } catch (FileNotFoundException e) {
                // expected
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Adds a new player to your playerlist.
     *
     * @param p A Player Object of the new player.
     */

    public void addPlayer(Player p) {
        Player player = getFirstPlayerByPortAndId(p.getPort(), p.getPlayerNumber());
        if (player != null && player.getPlayerNumber() != -1) {
            removePlayerByPortAndId(p.getPort(), p.getPlayerNumber());
        }
        logger.info("Add player " + p.getName() + " with id " + p.getPlayerNumber() + " from port " + p.getPort() + ".");
        this.playerList.add(p);
    }

    private boolean removePlayerByPortAndId(String port, int playerNumber) {
        for (int i = 0; i < this.playerList.size(); i++) {
            if (playerList.get(i).getPlayerNumber() == playerNumber && port.equals(playerList.get(i).getPort())) {
                logger.info("Remove player " + playerList.get(i).getName() + " with id " + playerList.get(i).getPlayerNumber() + " from port " + playerList.get(i).getPort() + ".");
                this.playerList.remove(i);
                return true;
            }
        }
        return false;
    }

    /**
     * This method search a player by his vaporID and return the player Object.
     *
     * @param vapor The vaporID of the player you search.
     * @return Returns a player Object if found in the playerlist.
     */

    public Player getPlayerByVaporID(String vapor) {
        for (Player aPlayerList : this.playerList) {
            if (vapor.equals(aPlayerList.getVapor()))
                return aPlayerList;
        }
        logger.debug("No player found by vapor '" + vapor + "' , asking server for update of playerlist.");
        ServerOperations.sendServerStatusCommand();
        for (Player aPlayerList : this.playerList) {
            if (vapor.equals(aPlayerList.getVapor()))
                return aPlayerList;
        }
        logger.debug("Still no player found, possible bug?");
        return null;
    }

    /**
     * This method will tell you if there's a player with a specific Nickname on your server.
     *
     * @param name The name of the player you searched
     * @return Returns true, if a player with the specific Nickname is found on one of your servers.
     */

    public boolean isPlayerByName(String name) {
        return this.getPlayerByName(name) != null;
    }

    /**
     * This method
     *
     * @param name .
     * @return Returns a playerobject of a player with the specific name
     */

    public Player getPlayerByName(String name) {
        for (Player aPlayerList : this.playerList) {
            if (aPlayerList.getName().equals(name)) return aPlayerList;
        }
        logger.debug("No player found by name '" + name + "', asking server for update of playerlist.");
        ServerOperations.sendServerStatusCommand();
        for (Player aPlayerList : this.playerList) {
            if (aPlayerList.getName().equals(name)) return aPlayerList;
        }
        logger.debug("Still no player found, possible bug?");
        return null;
    }

    /**
     * Sets a player's teamID to a new one.
     *
     * @param port         The port of the virtual server, the player is playing on.
     * @param playerNumber The player's playerID
     * @param team         The new team of the player
     */

    public void setPlayersTeam(String port, int playerNumber, int team) {
        int i = 0;
        if (playerList.size() > 0) {
            for (Player p : playerList) {
                if (port.equals(p.getPort()) && playerNumber == p.getPlayerNumber()) {
                    p.setTeam(team);
                }
            }
        }
    }

    /**
     * The method removes one player from the playerlist. Note that it doesn't actually
     * kick the player, it just removes it from the playerlist.
     *
     * @param vapor The vaporID of the player you want to remove from your playerlist.
     * @return Returns true, if a player got removed from the playerlist.
     */

    public boolean removePlayerByVaporID(String vapor) {
        for (int i = 0; i < this.playerList.size(); i++) {
            if (vapor.equals((this.playerList.get(i)).getVapor())) {
                logger.info("Remove player " + playerList.get(i).getName() + " with id " + playerList.get(i).getPlayerNumber() + " from port " + playerList.get(i).getPort() + ".");
                this.playerList.remove(i);
                return true;
            }
        }
        return false;
    }

    /**
     * This method will give you an array of player on a specific virtual server.
     * If the port you enter isn't a port a server, it will return an empty array
     *
     * @param port The port of a virtual server you want a Player array from.
     * @return An array of players on the server specified by the port parameter.
     */

    public Player[] getPlayersByPort(String port) {
        LinkedList<Player> tmp = new LinkedList<Player>();
        for (Player aPlayerList : this.playerList) {
            if (aPlayerList.getPort().equals(port))
                tmp.add(aPlayerList);
        }
        Player ptmp[] = new Player[tmp.size()];
        for (int h = 0; h < tmp.size(); h++) {
            ptmp[h] = tmp.get(h);
        }
        return ptmp;
    }

    /**
     * This method will return an unsorted array containing all the players on your altitude server.
     * This also includes bots.
     *
     * @return Returns an array containing all players on every virtual server you host
     */

    public Player[] getPlayerArray() {
        Player[] players = new Player[this.playerList.size()];
        for (int i = 0; i < this.playerList.size(); i++) {
            players[i] = this.playerList.get(i);
        }
        return players;
    }

    public Player[] getPlayersByPortAndId(String port, int id) {
        LinkedList<Player> tmp = new LinkedList<Player>();
        for (Player aPlayerList : this.playerList) {
            if (aPlayerList.getPort().equals(port) && (aPlayerList.getPlayerNumber()) == id)
                tmp.add(aPlayerList);
        }
        Player ptmp[] = new Player[tmp.size()];
        for (int h = 0; h < tmp.size(); h++) {
            ptmp[h] = tmp.get(h);
        }
        return ptmp;
    }

    public Player getFirstPlayerByPortAndId(String port, int id) {
        Player[] players = getPlayersByPortAndId(port, id);
        if (players.length > 0) {
            return players[0];
        }
        return new Player("unknown", "0", "0", port, id);
    }
}
